Skip to content

feat(apl): generic-HTTP authorization#110

Open
araujof wants to merge 3 commits into
devfrom
feat/http_ext_fields
Open

feat(apl): generic-HTTP authorization#110
araujof wants to merge 3 commits into
devfrom
feat/http_ext_fields

Conversation

@araujof

@araujof araujof commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Engine-side additions that let CPEX authorize generic (non-MCP/A2A) HTTP requests. These are consumed by the Praxis policy filter's new experimental enforcement: http mode to run Kuadrant-AuthPolicy-style authorization (see the companion Praxis PR). All changes are additive and non-breaking — existing MCP/entity behavior is unchanged.

Draft: pairs with the Praxis AuthPolicy spike praxis-proxy/praxis#746. Cut a release from this branch before Praxis pins it (Praxis currently consumes it via a temporary path dep).

What's here

  • HTTP request line on HttpExtension. Optional method/path/host/scheme, surfaced in the APL bag as http.method/http.path/http.host/http.scheme. Ride the existing read_headers capability (the http slot is gated as a whole in filter_extensions; a base-tier split would need granular sub-field filtering — deferred). host is documented to come from a validated authority, never a raw client Host.
  • custom denial response via PluginViolation.details. A per-route response: block (transpiled denyWith) is carried on CompiledRoute (additive; most-specific layer wins) and, on Decision::Deny, stashed into the existing details map under http.status / http.body / http.headers. No new fields on PluginViolation, no new APL grammar — reason-only denies are unchanged.
  • evaluate the global policy for entity-less requests. New reserved coordinates ENTITY_HTTP / ENTITY_NAME_GLOBAL and the cmf.http_request hook. The visitor installs a Pre-phase handler bound to the compiled global policy under those coordinates (granted read_headers), so a host can authorize a request that carries no MCP entity. Entity routes still stack global via apply_layer.

Tests

  • apl-cmf: request-line surfacing in the bag.
  • apl-core: DenyResponse apply_layer merge.
  • apl-cpex: response: sub-block parsing; and a new end-to-end global_http_authz test exercising everything together — allow, deny, and custom denyWith for an entity-less HTTP request.

Full apl-core / apl-cpex / cpex-core suites green; clippy + fmt clean.

araujof added 3 commits June 30, 2026 11:27
Add optional method/path/host/scheme to HttpExtension and surface them in
the APL bag as http.method/path/host/scheme. These let CEL/APL policies
reason over the HTTP request line — needed by the Praxis AuthPolicy
transpiler, where Kuadrant predicates over request.method/path/host map to
http.* (Praxis spike Phase B / U1).

The request line rides the existing read_headers capability: the `http`
extension slot is gated as a whole in cpex-core's filter_extensions, so a
base-tier split would require granular http sub-field filtering (deferred).
The host field is documented to be populated from a validated authority
(e.g. HTTP/2 :authority), never a raw client Host header, so host-based
policy cannot be bypassed.

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Add a per-route `response:` block (the transpiled form of a Kuadrant
AuthPolicy `denyWith`) that lets a route declare a custom HTTP status,
body, and headers for its denials (Praxis spike Phase B / U2).

- New optional DenyResponse on CompiledRoute (additive; most-specific
  layer wins in apply_layer). Read out-of-band from the route YAML by the
  apl-cpex visitor, like the `policy:` block — cpex-core tolerates the key.
- On Decision::Deny, route_handler stashes status/body/headers into the
  existing PluginViolation.details map under http.status / http.body /
  http.headers. No new fields on PluginViolation and no new APL grammar —
  the violation type stays stable and reason-only denies are unchanged.

A host (e.g. the Praxis policy filter) reads details to render a custom
denial response; absent → host default behavior.

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Make the catch-all `global` policy enforce on generic (non-MCP/A2A) HTTP
requests, which carry no entity (Praxis spike Phase B / U3).

- New reserved coordinates: ENTITY_HTTP ("http") / ENTITY_NAME_GLOBAL ("*")
  and the HOOK_CMF_HTTP_REQUEST ("cmf.http_request") hook.
- The visitor installs a Pre-phase AplRouteHandler bound to the compiled
  global policy under those coordinates, granted read_headers so the
  policy can read the request line/headers. Entity routes still stack
  `global` via apply_layer; this adds the entity-less evaluation path.
- A global-scope `response:` block (transpiled denyWith) is carried onto
  the global handler and surfaced on deny via PluginViolation.details (U2).

A host fires invoke_named::<CmfHook>("cmf.http_request", ...) with
meta.entity_type/name set to the reserved coordinates. End-to-end tests
cover allow, deny, and custom-denyWith — exercising U1 + U2 + U3 together.

Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
@araujof araujof requested a review from terylt July 1, 2026 00:57
@araujof araujof added enhancement New feature or request framework Rust labels Jul 1, 2026
@araujof araujof added this to CPEX Jul 1, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in CPEX Jul 1, 2026
@araujof araujof added this to the 0.2.1 milestone Jul 1, 2026
@araujof araujof marked this pull request as ready for review July 1, 2026 00:57
@araujof araujof requested a review from jonpspri as a code owner July 1, 2026 00:57
@araujof araujof changed the title feat(apl): generic-HTTP authorization (request line, denyWith, global eval) feat(apl): generic-HTTP authorization Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request framework Rust

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants